//初始化头文件
#include <linux/init.h>
//最基本的文件，支持动态添加和卸载模块。
#include <linux/module.h>
//平台设备所需要的头文件
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/ioport.h>
//文件系统头文件，定义文件表结构（file,buffer_head,m_inode 等）
#include <linux/fs.h>
//包含了 copy_to_user、copy_from_user 等内核访问用户进程内存地址的函数定义。
#include <linux/uaccess.h>
//包含了 ioremap、iowrite 等内核访问 IO 内存等函数的定义。
#include <linux/io.h>
#include <asm/mach/map.h>
//设备树of函数
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>

//杂项设备
#include <linux/miscdevice.h> 
#include <linux/types.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <linux/delay.h>

struct device_node *devicetree_platform_node; // 节点

// device_tree匹配不上时使用id_table进行匹配 优先级1
static const struct of_device_id devicetree_platform_of_match[] = {
	{ .compatible = "devicetree_platform", }, 
	{ },
};

// 该节点匹配不上时使用id_table进行匹配 优先级2
static const struct platform_device_id devicetree_platform_id_table = {
	.name = "device_platform" 
};

/*
 * @description     : 打开设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp    : 设备文件，file结构体有个叫做private_data的成员变量
 *                    一般在open的时候将private_data指向设备结构体。
 * @return          : 0 成功;其他 失败
 */
static int misc_open (struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "hello misc_open!\n");
    return 0;
}

/*
 * @description     : 关闭/释放设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp    : 要关闭的设备文件(文件描述符)
 * @return          : 0 成功; 其他 失败
 */
static int misc_release(struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "bye bye misc_release!\n");
    return 0;
}

/*
 * @description     : 从设备读取数据 
 * @param - filp    : 要打开的设备文件(文件描述符)
 * @param - buf     : 返回给用户空间的数据缓冲区
 * @param - cnt     : 要读取的数据长度
 * @param - offt    : 相对于文件首地址的偏移
 * @return          : 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t misc_read(struct file *file, char __user *ubuf, size_t cnt, loff_t *offt)
{
    int ret;
    char data[12] = "hello misc!";
    ret = copy_to_user(ubuf, data, sizeof(data));

    printk(KERN_EMERG "hello misc_read!\n");
    return ret;
}

/*
 * @description     : 从设备节点写入数据 
 * @param - filp    : 要打开的设备文件(文件描述符)
 * @param - buf     : 返回给用户空间的数据缓冲区
 * @param - cnt     : 要写入的数据长度
 * @param - offt    : 相对于文件首地址的偏移
 * @return          : 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t misc_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *offt)
{
    char kbuf[64] = "copy from user!\n";
    if( copy_from_user(kbuf, ubuf, cnt) != 0 ){
        printk(KERN_EMERG "copy_from_user error!\n");
        return -1;
    }
    printk("buf is:%s\n", kbuf);

    printk(KERN_EMERG "hello misc_write!\n");
    return 0;
}

/* 操作函数结构体 */
struct file_operations misc_fops = {
    .owner      = THIS_MODULE,
    .open       = misc_open,
    .release    = misc_release,
    .read       = misc_read,
    .write      = misc_write,
};


/* 杂项设备结构体 */
struct miscdevice misc_dev = {
    .minor  = MISC_DYNAMIC_MINOR,     /* 次设备号 */
    .name   = "misc_name",            /* 设备名/dev/下的设备节点名   */
    .fops   = &misc_fops              /* 设备文件操作方法 */
};


static int devicetree_platform_probe(struct platform_device *pdev)
{
	int ret;
	struct property *compatible_property; // compatible属性
	u32 regdata[10]; // reg数组
	
	devicetree_platform_node = of_find_node_by_path("/devicetree_platform"); // 需要注意"devicetree_platform"的节点必须写在根节点也就是需要对应路径
	if(devicetree_platform_node == NULL) {
		printk("devicetree_platform_node can not found!\r\n");
		return -EINVAL;
	} else {
		printk("devicetree_platform_node has been found!\r\n");
	}
	/* 获取 compatible 属性内容 */
	compatible_property = of_find_property(devicetree_platform_node, "compatible", NULL);
	if(compatible_property == NULL) {
		printk("compatible property find failed\r\n");
	} else {
		printk("compatible = %s\r\n", (char*)compatible_property->value);
	}
	
	/* 获取 reg 属性内容 reg[0] = 0x020c406c reg[1] = 0x04 ....*/
	ret = of_property_read_u32_array(devicetree_platform_node, "reg", regdata, 10);
	if(ret < 0) {
		printk("reg property read failed!\r\n");
	} else {
		u8 i = 0;
		printk("reg data:\r\n");
		for(i = 0; i < 10; i++)
		printk("%#X ", regdata[i]);
		printk("\r\n");
	}
	
    ret = misc_register(&misc_dev);

    if(ret < 0){
        printk(KERN_EMERG "misc_register failed!\n");
        return -1;
    }

	return 0;
}
static int devicetree_platform_remove(struct platform_device *pdev)
{
	printk("%s %d\n", __FUNCTION__, __LINE__);
	return 0;
}


static struct platform_driver devicetree_platform_driver = {
	.probe		= devicetree_platform_probe,
	.remove		= devicetree_platform_remove,
	.driver		= {
		.name	= "devicetree_platform",  // 优先级3
		.of_match_table = of_match_ptr(devicetree_platform_of_match),
	},
	.id_table 	= &devicetree_platform_id_table	// 该节点匹配不上时使用name属性进行匹配 优先级2
};


/* 1. 入口函数 */
static int __init devicetree_platform_init(void)
{	
	printk("%s %d\n", __FUNCTION__, __LINE__);
	/* 1.1 注册一个platform_driver */
	return platform_driver_register(&devicetree_platform_driver);
}


/* 2. 出口函数 */
static void __exit devicetree_platform_exit(void)
{
	printk("%s %d\n", __FUNCTION__, __LINE__);
	/* 2.1 反注册platform_driver */
	platform_driver_unregister(&devicetree_platform_driver);
	/* 杂项设备释放 */
	misc_deregister(&misc_dev);
}

module_init(devicetree_platform_init);
module_exit(devicetree_platform_exit);


MODULE_AUTHOR("Kongjun"); // 作者
MODULE_DESCRIPTION("platform driver"); // 驱动介绍可以不写
MODULE_LICENSE("GPL");// 开源协议